package com.liuxing.calculation;

import com.liuxing.Util.StringUtils;
import com.liuxing.entity.CalculationEngine;
import com.liuxing.service.CalculationEngineService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * desc: 动态脚本计算引擎
 *
 * @author 刘磊
 * @since 2020/3/16
 */
@Slf4j
@Component
public abstract class ICalculationEngine {

    @Autowired
    private CalculationEngineService calculationEngineService;

    /**
     * 根据数据库配置的脚本ID获取脚本计算
     *
     * @param id    脚本ID
     * @param param 参数
     * @return 计算结果
     */
    public Object calculate(Integer id, Map<String, Object> param) {
        CalculationEngine data = calculationEngineService.selectByPrimaryKey(id);
        return calculate(data, param);
    }

    public Object calculate(CalculationEngine data, Map<String, Object> param) {
        // 脚本内容
        String script = data.getContent();
        if (script == null || "".equals(script)) {
            log.error("脚本内容为空，请检查脚本配置。");
            return null;
        }
        // 需要将数据源放到参数列表里面
        if (StringUtils.isNotEmpty(data.getDatabaseId())) {
            param.put("databaseId", data.getDatabaseId());
        }
        // 有父脚本优先计算父脚本
        if (data.getParentId() != null) {
            calculateParent(data.getParentId(), param);
        }
        return calculate(script, param);
    }

    /**
     * 根据脚本内容计算结果，具体的计算逻辑由子类来实现
     *
     * @param content 脚本内容
     * @param param   参数
     * @return 计算结果
     */
    abstract Object calculate(String content, Map<String, Object> param);

    /**
     * 递归计算所有父脚本
     *
     * @param parentId 父脚本ID
     * @param param    参数
     */
    private void calculateParent(int parentId, Map<String, Object> param) {
        CalculationEngine data = calculationEngineService.selectByPrimaryKey(parentId);
        if (StringUtils.isNotEmpty(data.getContent())) {
            Object obj = calculate(data.getContent(), param);
            if (obj == null) {
                log.info("[pid={}]计算结果异常", data.getId());
                return;
            }
            // 计算结果
            setParentResultToParamMap(parentId, obj, param);
        }
        // 递归计算
        if (data.getParentId() != null) {
            calculateParent(data.getParentId(), param);
        }
    }

    /**
     * 将父脚本的计算结果放到参数列表中，由于计算结果可能形式多样，如果子类返回结果方式特殊，需要覆盖此方法, 这里提供默认实现
     *
     * @param parentId 父脚本ID
     * @param obj      父脚本计算结果
     * @param param    参数列表
     */
    protected void setParentResultToParamMap(Integer parentId, Object obj, Map<String, Object> param) {
        param.put("pid" + parentId, obj);
    }

}
